class Array
  def var(n) # make variations of n elements
    return [[]] if n == 0 # there is one combination of 0 elements: the empty set
    return collect { |e| [e] } if n == 1
    variations = []
    var(n - 1).each do |v|
      self.each { |el| variations << (v + [el]) unless v.include?(el) }
    end
    
    variations
  end

  def comb(n) # make combinations of n elements
    return [[]] if n == 0 # there is one combination of 0 elements: the empty set
    return collect { |e| [e] } if n == 1
    combinations = []
    comb(n - 1).each do |c|
      self[(index(c.last) + 1)...size].each { |el| combinations << (c + [el]) unless c.include?(el) }
    end
    
    combinations
  end
  
  def power_set(allowed_sizes = nil)
    allowed_sizes ||= 0..size # can't use method calls as default argument values I suppose
    allowed_sizes.collect { |s| comb(s) }.union
  end

  def random
    self[rand(size)]
  end
  
  def min_by
    # sort_by { |e| yield e }.first
    # but this is slow apparently. so another implementation:
    return nil if size == 0
    heuristics = collect { |e| yield e }
    self[heuristics.index(heuristics.min)]
  end
  
  def max_by
    # sort_by { |e| yield e }.last
    # but this is slow apparently. so another implementation:
    return nil if size == 0
    heuristics = collect { |e| yield e }
    self[heuristics.index(heuristics.max)]
  end
  
  def sum
    if block_given?
      inject(0) { |sum, e| sum + (yield e) }
    else
      sum { |e| e }
    end
  end
  
  def union
    inject([]) { |u, a| u | a }
  end
  
  def intersection
    inject(union) { |i, a| i & a }
  end
  
  def disjoint?
    intersection.empty?
  end
  
  def intersect?
    !disjoint?
  end
  
  def all_equal?
    all? { |e| first == e } # all elements in the array are equal
  end
end


class Symbol
  def to_proc
    Proc.new { |obj, *args| obj.send(self, *args) }
  end
end
